#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Muto Robot Controller API Routes

This module contains all FastAPI route definitions for the Muto Robot Controller.
"""

import time
import asyncio
import json
from typing import Dict, Any
from fastapi import FastAPI, HTTPException, Query
from starlette.concurrency import run_in_threadpool

from .models import (
    ExecutePlanRequest, ExecutePlanResponse, ExecutedCommand,
    PlayVoiceRequest, PlayVoiceResponse,
    JSONPlanRequest, JSONPlanResponse,
    WrappedPlanRequest,
    VoiceModuleStatusResponse, PlaySystemSoundRequest, PlaySystemSoundResponse,
    TTSRequest, TTSResponse,
    PlanStatus,
    # 记忆功能相关模型
    WriteLongTermMemoryRequest, WriteLongTermMemoryResponse,
    ReadLongTermMemoryRequest, ReadLongTermMemoryResponse,
    WriteShortTermMemoryRequest, WriteShortTermMemoryResponse,
    ReadShortTermMemoryRequest, ReadShortTermMemoryResponse,
    ClearShortTermMemoryRequest, ClearShortTermMemoryResponse,
    MemoryStatusResponse,
    # 热身蹲起动作相关模型
    WarmUpSquatRequest, WarmUpSquatResponse,
    # 举手动作相关模型
    RaiseHandRequest, RaiseHandResponse,
    # 指向前方动作相关模型
    PointFrontRequest, PointFrontResponse,
    # 全局取消/恢复控制
    CancelControlResponse, ResumeControlResponse
)
from .command_executor import MutoCommandExecutor
from .voice_assistant_service import IntegratedVoiceAssistantService


class MutoAPIRoutes:
    """Muto机器人API路由管理器"""
    
    def __init__(self, app: FastAPI, command_executor: MutoCommandExecutor, logger=None):
        self.app = app
        self.command_executor = command_executor
        self.logger = logger
        try:
            self.voice_assistant_service = IntegratedVoiceAssistantService(self.command_executor)
        except Exception:
            self.voice_assistant_service = None
        self.setup_routes()
    
    def setup_routes(self):
        """设置FastAPI路由"""
        COMPACT_LIMIT = 50

        @self.app.middleware("http")
        async def json_logging_middleware(request, call_next):
            start_ts = time.time()
            path = request.url.path
            method = request.method
            try:
                response = await call_next(request)
                duration = time.time() - start_ts
                entry = {
                    "event": "http_response",
                    "path": path,
                    "method": method,
                    "status_code": getattr(response, "status_code", None),
                    "duration_ms": int(duration * 1000)
                }
                if self.logger:
                    try:
                        self.logger.info(json.dumps(entry, ensure_ascii=False))
                    except Exception:
                        pass
                return response
            except Exception as e:
                duration = time.time() - start_ts
                entry = {
                    "event": "http_error",
                    "path": path,
                    "method": method,
                    "error": str(e),
                    "duration_ms": int(duration * 1000)
                }
                if self.logger:
                    try:
                        self.logger.error(json.dumps(entry, ensure_ascii=False))
                    except Exception:
                        pass
                raise

        def _compact_executed_commands(full_results):
            compact_list = []
            for cmd_result in full_results[:COMPACT_LIMIT]:
                desc = cmd_result.get('description', '')
                res = cmd_result.get('result', {})
                compact_list.append(ExecutedCommand(
                    description=desc,
                    data={'success': res.get('success', False)}
                ))
            return compact_list

        def _log_summary(event: str, success: bool, count: int, failed_id: str = None, duration: float = None):
            entry = {
                "event": event,
                "success": success,
                "executed_count": count,
                "failed_command_id": failed_id,
                "duration_ms": int(duration * 1000) if duration is not None else None
            }
            if self.logger:
                try:
                    self.logger.info(json.dumps(entry, ensure_ascii=False))
                except Exception:
                    pass
        
        @self.app.get("/")
        async def root():
            return {
                "message": "Muto Robot Controller API",
                "status": "running",
                "version": "1.0.0"
            }
        
        @self.app.get("/health")
        async def health_check():
            return {
                "status": "healthy",
                "muto_controller_available": self.command_executor.muto_interface is not None,
                "voice_interface_available": self.command_executor.voice_interface is not None,
                "ros2_node_active": True,
                "voice_assistant_running": bool(getattr(self, 'voice_assistant_service', None) and self.voice_assistant_service.is_running)
            }

        # ==================== 全局取消/恢复控制 ====================
        @self.app.post("/controller/cancel", response_model=CancelControlResponse)
        async def controller_cancel():
            """触发全局取消：打断语音播放/录音，并停止机器人当前动作"""
            if self.logger:
                self.logger.warning('Received global cancellation request')
            try:
                result = self.command_executor.request_cancel()
                response = CancelControlResponse(
                    success=result.get('success', False),
                    message=result.get('message', 'Unknown'),
                    canceled_at=time.time() if result.get('success', False) else None
                )
                if self.logger:
                    self.logger.info(f"Global cancellation processed: {response.success}")
                return response
            except Exception as e:
                error_msg = f"Error processing cancellation: {str(e)}"
                if self.logger:
                    self.logger.error(error_msg)
                raise HTTPException(status_code=500, detail=error_msg)

        @self.app.post("/controller/resume", response_model=ResumeControlResponse)
        async def controller_resume():
            """清除取消状态：允许后续请求继续执行"""
            if self.logger:
                self.logger.info('Received resume request')
            try:
                result = self.command_executor.clear_cancel()
                response = ResumeControlResponse(
                    success=result.get('success', False),
                    message=result.get('message', 'Unknown')
                )
                if self.logger:
                    self.logger.info(f"Resume processed: {response.success}")
                return response
            except Exception as e:
                error_msg = f"Error processing resume: {str(e)}"
                if self.logger:
                    self.logger.error(error_msg)
                raise HTTPException(status_code=500, detail=error_msg)

        @self.app.post("/voice_assistant/start")
        async def voice_assistant_start():
            try:
                if not self.voice_assistant_service:
                    raise HTTPException(status_code=503, detail="Voice assistant service not available")
                started = self.voice_assistant_service.start()
                return {"success": bool(started)}
            except Exception as e:
                raise HTTPException(status_code=500, detail=str(e))

        @self.app.post("/voice_assistant/stop")
        async def voice_assistant_stop():
            try:
                if not self.voice_assistant_service:
                    raise HTTPException(status_code=503, detail="Voice assistant service not available")
                stopped = self.voice_assistant_service.stop()
                return {"success": bool(stopped)}
            except Exception as e:
                raise HTTPException(status_code=500, detail=str(e))

        @self.app.get("/voice_assistant/status")
        async def voice_assistant_status():
            try:
                if not self.voice_assistant_service:
                    raise HTTPException(status_code=503, detail="Voice assistant service not available")
                return {
                    "success": True,
                    "is_running": self.voice_assistant_service.is_running,
                    "backend_processing": self.voice_assistant_service.backend_processing
                }
            except Exception as e:
                raise HTTPException(status_code=500, detail=str(e))
        
        @self.app.post("/execute_commands", response_model=JSONPlanResponse)
        async def execute_commands(request: JSONPlanRequest, compact: bool = Query(False, description="返回简洁模式")):
            """执行JSON格式的计划数据"""
            start_time = time.time()
            
            if self.logger:
                self.logger.info(f'Received JSON plan execution request with status: {request.status}')
            
            try:
                # 高优先级取消检查（新的策略）：
                # 如果取消标志已设置，但当前没有会话在运行，视为残留标志，自动清除并继续执行；
                # 如果有会话在运行，则拒绝新的计划请求。
                if self.command_executor.session_manager.is_cancel_active():
                    if self.command_executor.session_manager.plan_running:
                        if self.logger:
                            self.logger.warning('Cancellation in progress for current session. Rejecting new JSON plan request.')
                        execution_time = time.time() - start_time
                        return JSONPlanResponse(
                            success=False,
                            message='当前执行会话正在取消中，已拒绝新的计划请求。请稍后重试。',
                            executed_commands=[],
                            execution_time=execution_time,
                            failed_command_id=None
                        )
                    else:
                        # 自动清除残留取消标志
                        if self.logger:
                            self.logger.info('Residual cancel flag detected before new JSON plan. Auto-clearing it and proceeding.')
                        try:
                            self.command_executor.clear_cancel()
                        except Exception:
                            # 即使清除失败，也尝试继续执行
                            pass
                # 构造计划数据字典
                plan_data = {
                    'status': request.status,
                    'plan': request.plan,
                    'need_broadcast': request.need_broadcast
                }
                
                # 执行计划（在独立线程中运行以避免阻塞事件循环，从而让/cancel能及时响应）
                result = await self.command_executor.execute_plan(plan_data)
                
                execution_time = time.time() - start_time
                
                executed = result.get('executed_commands', [])
                if compact:
                    executed = [
                        {
                            'description': item.get('description', ''),
                            'success': item.get('result', {}).get('success', False)
                        }
                        for item in executed[:COMPACT_LIMIT]
                    ]
                _log_summary("execute_commands", result.get('success', False), len(executed), result.get('failed_command_id'), execution_time)
                response = JSONPlanResponse(
                    success=result['success'],
                    message=result.get('message', result.get('error', 'Unknown result')),
                    executed_commands=executed,
                    execution_time=execution_time,
                    failed_command_id=result.get('failed_command_id')
                )
                
                if self.logger:
                    self.logger.info(f'JSON plan execution completed: {result["success"]}')
                return response
                
            except Exception as e:
                execution_time = time.time() - start_time
                error_msg = f"Error executing JSON plan: {str(e)}"
                if self.logger:
                    self.logger.error(error_msg)
                raise HTTPException(status_code=500, detail=error_msg)
        
        @self.app.post("/execute_plan", response_model=ExecutePlanResponse)
        async def execute_plan(request: ExecutePlanRequest, compact: bool = Query(False, description="返回简洁模式")):
            """执行基于muto_controller的指令序列"""
            start_time = time.time()
            
            if self.logger:
                self.logger.info(f'Received plan execution request with status: {request.status}')
            
            try:
                if request.status == PlanStatus.FAILED:
                    return ExecutePlanResponse(
                        success=False,
                        message=f"Plan execution failed: status is {request.status}",
                        executed_commands=[],
                        execution_time=0.0
                    )
                # 高优先级取消检查（新的策略）：
                # 如果取消标志已设置，但当前没有会话在运行，视为残留标志，自动清除并继续执行；
                # 如果有会话在运行，则拒绝新的计划请求。
                if self.command_executor.session_manager.is_cancel_active():
                    if self.command_executor.session_manager.plan_running:
                        if self.logger:
                            self.logger.warning('Cancellation in progress for current session. Rejecting new plan request.')
                        execution_time = time.time() - start_time
                        return ExecutePlanResponse(
                            success=False,
                            message='当前执行会话正在取消中，已拒绝新的计划请求。请稍后重试。',
                            executed_commands=[],
                            failed_commands=[],
                            failed_command_id=None,
                            execution_time=execution_time,
                            error_details=[]
                        )
                    else:
                        # 自动清除残留取消标志
                        if self.logger:
                            self.logger.info('Residual cancel flag detected before new plan. Auto-clearing it and proceeding.')
                        try:
                            self.command_executor.clear_cancel()
                        except Exception:
                            pass
                
                # 构造计划数据字典，调用command_executor的execute_plan方法
                plan_data = {
                    'status': request.status.value if hasattr(request.status, 'value') else str(request.status),
                    'plan': [{
                        'id': item.id,
                        'command': item.command,
                        'description': item.description,
                        'dependencies': item.dependencies,
                        'critical': item.critical,
                        'parallel_group': getattr(item, 'parallel_group', None),
                        'sync_with': getattr(item, 'sync_with', [])
                    } for item in request.plan],
                    'response': getattr(request, 'response', '')
                }
                
                # 使用command_executor的execute_plan方法，在线程池中运行以避免阻塞事件循环
                # 这样/cancel可以在执行过程中及时被响应
                result = await self.command_executor.execute_plan(plan_data)
                
                execution_time = time.time() - start_time
                
                # 转换结果格式以匹配ExecutePlanResponse
                executed_commands = []
                failed_commands = []
                error_details = []
                message = result.get('message', result.get('error', ''))
                failed_command_id = result.get('failed_command_id')
                
                if result.get('success', False):
                    full_results = result.get('executed_commands', [])
                    if compact:
                        executed_commands = _compact_executed_commands(full_results)
                    else:
                        for cmd_result in full_results:
                            result_data = cmd_result.get('result', {})
                            executed_cmd = ExecutedCommand(
                                description=cmd_result.get('description', ''),
                                data=result_data
                            )
                            executed_commands.append(executed_cmd)
                else:
                    # 处理失败的命令
                    failed_commands = result.get('failed_commands', [])
                    error_details = result.get('error_details', failed_commands)
                
                overall_success = result.get('success', False)
                
                # 使用更新后的响应模型
                response = ExecutePlanResponse(
                    success=overall_success,
                    executed_commands=executed_commands,
                    failed_commands=failed_commands,
                    message=message,
                    failed_command_id=failed_command_id,
                    execution_time=execution_time,
                    error_details=error_details
                )
                _log_summary("execute_plan", overall_success, len(executed_commands), failed_command_id, execution_time)
                
                if self.logger:
                    if overall_success:
                        self.logger.info(f'Plan execution completed: {overall_success} (execution_time={execution_time:.3f}s)')
                    else:
                        # 输出详细失败原因到日志，便于排查
                        self.logger.error(f'Plan execution completed: {overall_success} (execution_time={execution_time:.3f}s)')
                        if message:
                            self.logger.error(f'Failure summary: {message}')
                        if failed_command_id:
                            self.logger.error(f'First failed command ID: {failed_command_id}')
                        if error_details:
                            # 将每条失败的命令与其错误信息打印出来
                            for idx, detail in enumerate(error_details, start=1):
                                cid = detail.get('command_id') or detail.get('id')
                                cmd = detail.get('command')
                                err = detail.get('error') or detail.get('message')
                                self.logger.error(f'[Failure {idx}] command_id={cid}, command={cmd}, error={err}')
                return response
                
            except Exception as e:
                execution_time = time.time() - start_time
                error_msg = f"Error executing plan: {str(e)}"
                if self.logger:
                    self.logger.error(error_msg)
                raise HTTPException(status_code=500, detail=error_msg)
        

        
        @self.app.post("/play_voice", response_model=PlayVoiceResponse)
        async def play_voice(request: PlayVoiceRequest):
            """播放指定语音"""
            if self.logger:
                self.logger.info(f'Received voice play request: {request.voice_type} - {request.content}')
            
            try:
                if not self.command_executor.voice_interface:
                    raise HTTPException(status_code=503, detail="Voice interface not available")
                
                # 高优先级取消检查：如果当前处于取消状态，立即停止播放并返回取消提示
                if self.command_executor.cancel_event.is_set():
                    try:
                        # 主动打断并停止任何正在进行的播放
                        self.command_executor.voice_interface.interrupt_audio_playback()
                        self.command_executor.voice_interface.stop_audio(force=True)
                    except Exception:
                        pass
                    if self.logger:
                        self.logger.warning('Playback request rejected due to active cancellation flag. Audio stopped immediately.')
                    return PlayVoiceResponse(
                        success=False,
                        message='Playback canceled',
                        voice_type=request.voice_type,
                        content=request.content
                    )
                
                # 调用执行器的语音播放方法
                result = self.command_executor.play_voice(
                    request.voice_type, 
                    request.content, 
                    request.volume
                )
                
                response = PlayVoiceResponse(
                    success=result['success'],
                    message=result['message'],
                    voice_type=result['voice_type'],
                    content=result['content']
                )
                
                if self.logger:
                    self.logger.info(f'Voice play result: {result["success"]} - {result["message"]}')
                return response
                
            except Exception as e:
                error_msg = f"Error playing voice: {str(e)}"
                if self.logger:
                    self.logger.error(error_msg)
                raise HTTPException(status_code=500, detail=error_msg)
        
        
        @self.app.post("/execute_wrapped_plan", response_model=ExecutePlanResponse)
        async def execute_wrapped_plan(request: WrappedPlanRequest):
            """执行包装格式的指令序列 - 处理外层包含result字段的请求"""
            if self.logger:
                self.logger.info(f'Received wrapped plan execution request with {len(request.result.plan)} commands')
                self.logger.info(f'Raw request data: {request.dict()}')
            
            # 提取内层的ExecutePlanRequest并调用原有的execute_plan逻辑
            return await execute_plan(request.result)
        
        
        @self.app.post("/debug_wrapped_plan")
        async def debug_wrapped_plan(request: dict):
            """调试端点 - 打印原始请求数据格式"""
            if self.logger:
                self.logger.info(f'Debug - Raw request type: {type(request)}')
                self.logger.info(f'Debug - Raw request content: {request}')
                if isinstance(request, dict):
                    self.logger.info(f'Debug - Request keys: {list(request.keys())}')
                    if 'result' in request:
                        self.logger.info(f'Debug - Result content: {request["result"]}')
                        self.logger.info(f'Debug - Result type: {type(request["result"])}')
            
            return {"status": "debug_complete", "received_data": request}
        
        
        @self.app.get("/voice_module/status", response_model=VoiceModuleStatusResponse)
        async def get_voice_module_status():
            """获取voice_module状态"""
            if self.logger:
                self.logger.info('Received voice module status request')
            
            try:
                if not self.command_executor.voice_interface:
                    return VoiceModuleStatusResponse(
                        success=False,
                        message="Voice interface not available"
                    )
                
                # 获取模块状态
                success, message, module_status = self.command_executor.voice_interface.get_module_status()
                if not success:
                    return VoiceModuleStatusResponse(
                        success=False,
                        message=f"Failed to get module status: {message}"
                    )
                
                # 获取队列状态
                queue_success, queue_message, queue_status = self.command_executor.voice_interface.get_queue_status()
                
                response = VoiceModuleStatusResponse(
                    success=True,
                    message="Voice module status retrieved successfully",
                    module_status=module_status,
                    queue_status=queue_status if queue_success else None
                )
                
                if self.logger:
                    self.logger.info(f'Voice module status: {module_status}')
                return response
                
            except Exception as e:
                error_msg = f"Error getting voice module status: {str(e)}"
                if self.logger:
                    self.logger.error(error_msg)
                return VoiceModuleStatusResponse(
                    success=False,
                    message=error_msg
                )
        
        
        @self.app.post("/voice_module/play_system_sound", response_model=PlaySystemSoundResponse)
        async def play_system_sound(request: PlaySystemSoundRequest):
            """播放系统音乐"""
            if self.logger:
                self.logger.info(f'Received system sound play request: {request.sound_name}')
            
            try:
                if not self.command_executor.voice_interface:
                    return PlaySystemSoundResponse(
                        success=False,
                        message="Voice interface not available",
                        sound_name=request.sound_name,
                        language=self.command_executor.voice_interface.language if self.command_executor.voice_interface else "unknown"
                    )
                
                # 调用voice_interface播放系统声音（语言由voice_interface内部决定）
                success, message, _ = self.command_executor.voice_interface.play_system_sound(
                    request.sound_name
                )
                
                response = PlaySystemSoundResponse(
                    success=success,
                    message=message,
                    sound_name=request.sound_name,
                    language=self.command_executor.voice_interface.language
                )
                
                if self.logger:
                    self.logger.info(f'System sound play result: {response.success} - {response.message}')
                return response
                
            except Exception as e:
                error_msg = f"Error playing system sound: {str(e)}"
                if self.logger:
                    self.logger.error(error_msg)
                return PlaySystemSoundResponse(
                    success=False,
                    message=error_msg,
                    sound_name=request.sound_name,
                    language=self.command_executor.voice_interface.language
                )
        
        @self.app.post("/voice_module/text_to_speech", response_model=TTSResponse)
        async def text_to_speech(request: TTSRequest):
            """文本转语音接口"""
            if self.logger:
                self.logger.info(f'Received TTS request: {request.text[:50]}...')
            
            try:
                if not self.command_executor.voice_interface:
                    return TTSResponse(
                        success=False,
                        message="Voice interface not available",
                        text=request.text
                    )
                
                # 调用command_executor的TTS方法
                result = await self.command_executor.synthesize_speech(
                    text=request.text,
                    output_filename=request.output_filename,
                    volume=request.volume,
                    max_retries=request.max_retries,
                    save_file=request.save_file
                )
                
                response = TTSResponse(
                    success=result['success'],
                    message=result['message'],
                    text=request.text,
                    output_file=result.get('output_file'),
                    audio_duration=result.get('audio_duration'),
                    file_size=result.get('file_size'),
                    synthesis_time=result.get('synthesis_time')
                )
                
                if self.logger:
                    self.logger.info(f'TTS result: {response.success} - {response.message}')
                return response
                
            except Exception as e:
                error_msg = f"Error in text to speech: {str(e)}"
                if self.logger:
                    self.logger.error(error_msg)
                return TTSResponse(
                    success=False,
                    message=error_msg,
                    text=request.text
                )
        
        # ... existing code ...
        
        @self.app.post("/memory/long_term/write", response_model=WriteLongTermMemoryResponse)
        async def write_long_term_memory(request: WriteLongTermMemoryRequest):
            """写入长期记忆"""
            # 如果没有提供时间戳，自动生成当前时间戳
            timestamp = request.timestamp or time.strftime("%Y-%m-%d %H:%M:%S")
            
            if self.logger:
                self.logger.info(f'Received long-term memory write request with timestamp: {timestamp}')
            
            try:
                if not self.command_executor.muto_interface:
                    return WriteLongTermMemoryResponse(
                        success=False,
                        message="Muto interface not available",
                        timestamp=timestamp
                    )
                
                # 调用MutoController的长期记忆写入方法
                success, message = self.command_executor.muto_interface.write_long_term_memory(
                    timestamp,
                    request.content
                )
                
                response = WriteLongTermMemoryResponse(
                    success=success,
                    message=message,
                    timestamp=timestamp
                )
                
                if self.logger:
                    self.logger.info(f'Long-term memory write result: {success} - {message} - timestamp: {timestamp}')
                return response
                
            except Exception as e:
                error_msg = f"Error writing long-term memory: {str(e)}"
                if self.logger:
                    self.logger.error(error_msg)
                return WriteLongTermMemoryResponse(
                    success=False,
                    message=error_msg,
                    timestamp=timestamp
                )
        
        @self.app.post("/memory/long_term/read", response_model=ReadLongTermMemoryResponse)
        async def read_long_term_memory(request: ReadLongTermMemoryRequest):
            """读取长期记忆"""
            if self.logger:
                self.logger.info(f'Received long-term memory read request: timestamp={request.timestamp}, start_time={request.start_time}, end_time={request.end_time}')
            
            try:
                if not self.command_executor.muto_interface:
                    return ReadLongTermMemoryResponse(
                        success=False,
                        message="Muto interface not available"
                    )
                
                # 调用MutoController的长期记忆读取方法
                success, data = self.command_executor.muto_interface.read_long_term_memory(
                    request.timestamp, 
                    request.start_time, 
                    request.end_time,
                    request.limit
                )
                
                if success:
                    if isinstance(data, list):
                        # 多个记忆
                        response = ReadLongTermMemoryResponse(
                            success=True,
                            message=f"Retrieved {len(data)} long-term memories",
                            memories=data
                        )
                    else:
                        # 单个记忆
                        response = ReadLongTermMemoryResponse(
                            success=True,
                            message="Long-term memory retrieved successfully",
                            memories=[data]
                        )
                else:
                    response = ReadLongTermMemoryResponse(
                        success=False,
                        message=data  # data contains error message when success is False
                    )
                
                if self.logger:
                    self.logger.info(f'Long-term memory read result: {success}')
                return response
                
            except Exception as e:
                error_msg = f"Error reading long-term memory: {str(e)}"
                if self.logger:
                    self.logger.error(error_msg)
                return ReadLongTermMemoryResponse(
                    success=False,
                    message=error_msg
                )
        
        @self.app.post("/memory/short_term/write", response_model=WriteShortTermMemoryResponse)
        async def write_short_term_memory(request: WriteShortTermMemoryRequest):
            """写入短期记忆"""
            # 如果没有提供时间戳，自动生成当前时间戳
            timestamp = request.timestamp or time.strftime("%Y-%m-%d %H:%M:%S")
            
            if self.logger:
                self.logger.info(f'Received short-term memory write request with timestamp: {timestamp}')
            
            try:
                if not self.command_executor.muto_interface:
                    return WriteShortTermMemoryResponse(
                        success=False,
                        message="Muto interface not available",
                        timestamp=timestamp,
                        ttl_seconds=request.ttl_seconds
                    )
                
                # 调用MutoController的短期记忆写入方法
                success, message, returned_timestamp = self.command_executor.muto_interface.write_short_term_memory(
                    request.content, 
                    timestamp,
                    request.ttl_seconds
                )
                
                response = WriteShortTermMemoryResponse(
                    success=success,
                    message=message,
                    timestamp=returned_timestamp or timestamp,
                    ttl_seconds=request.ttl_seconds
                )
                
                if self.logger:
                    self.logger.info(f'Short-term memory write result: {success} - {message} - timestamp: {returned_timestamp or timestamp}')
                return response
                
            except Exception as e:
                error_msg = f"Error writing short-term memory: {str(e)}"
                if self.logger:
                    self.logger.error(error_msg)
                return WriteShortTermMemoryResponse(
                    success=False,
                    message=error_msg,
                    timestamp=timestamp,
                    ttl_seconds=request.ttl_seconds
                )
        
        @self.app.post("/memory/short_term/read", response_model=ReadShortTermMemoryResponse)
        async def read_short_term_memory(request: ReadShortTermMemoryRequest):
            """读取短期记忆"""
            if self.logger:
                self.logger.info(f'Received short-term memory read request: timestamp={request.timestamp}, start_time={request.start_time}, end_time={request.end_time}')
            
            try:
                if not self.command_executor.muto_interface:
                    return ReadShortTermMemoryResponse(
                        success=False,
                        message="Muto interface not available"
                    )
                
                # 调用MutoController的短期记忆读取方法
                success, data = self.command_executor.muto_interface.read_short_term_memory(
                    request.timestamp,
                    request.start_time,
                    request.end_time
                )
                
                if success:
                    if isinstance(data, list):
                        # 多个记忆
                        response = ReadShortTermMemoryResponse(
                            success=True,
                            message=f"Retrieved {len(data)} short-term memories",
                            memories=data
                        )
                    else:
                        # 单个记忆
                        response = ReadShortTermMemoryResponse(
                            success=True,
                            message="Short-term memory retrieved successfully",
                            memories=[data]
                        )
                else:
                    response = ReadShortTermMemoryResponse(
                        success=False,
                        message=data  # data contains error message when success is False
                    )
                
                if self.logger:
                    self.logger.info(f'Short-term memory read result: {success}')
                return response
                
            except Exception as e:
                error_msg = f"Error reading short-term memory: {str(e)}"
                if self.logger:
                    self.logger.error(error_msg)
                return ReadShortTermMemoryResponse(
                    success=False,
                    message=error_msg
                )
        
        @self.app.post("/memory/short_term/clear", response_model=ClearShortTermMemoryResponse)
        async def clear_short_term_memory(request: ClearShortTermMemoryRequest):
            """清除短期记忆"""
            if self.logger:
                self.logger.info(f'Received short-term memory clear request: timestamp={request.timestamp}, start_time={request.start_time}, end_time={request.end_time}')
            
            try:
                if not self.command_executor.muto_interface:
                    return ClearShortTermMemoryResponse(
                        success=False,
                        message="Muto interface not available"
                    )
                
                # 调用MutoController的短期记忆清除方法
                success, message = self.command_executor.muto_interface.clear_short_term_memory(
                    request.timestamp,
                    request.start_time,
                    request.end_time
                )
                
                # 从消息中提取清除数量（如果有的话）
                cleared_count = None
                if success and "条" in message:
                    try:
                        # 尝试从消息中提取数字
                        import re
                        match = re.search(r'(\d+)\s*条', message)
                        if match:
                            cleared_count = int(match.group(1))
                    except:
                        pass
                
                response = ClearShortTermMemoryResponse(
                    success=success,
                    message=message,
                    cleared_count=cleared_count
                )
                
                if self.logger:
                    self.logger.info(f'Short-term memory clear result: {success} - {message}')
                return response
                
            except Exception as e:
                error_msg = f"Error clearing short-term memory: {str(e)}"
                if self.logger:
                    self.logger.error(error_msg)
                return ClearShortTermMemoryResponse(
                    success=False,
                    message=error_msg
                )
        
        @self.app.get("/memory/status", response_model=MemoryStatusResponse)
        async def get_memory_status():
            """获取记忆系统状态"""
            if self.logger:
                self.logger.info('Received memory status request')
            
            try:
                if not self.command_executor.muto_interface:
                    return MemoryStatusResponse(
                        success=False,
                        message="Muto interface not available"
                    )
                
                # 调用MutoController的记忆状态获取方法
                success, status = self.command_executor.muto_interface.get_memory_status()
                
                response = MemoryStatusResponse(
                    success=success,
                    message="Memory status retrieved successfully" if success else status,
                    status=status if success else None
                )
                
                if self.logger:
                    self.logger.info(f'Memory status result: {success}')
                return response
                
            except Exception as e:
                error_msg = f"Error getting memory status: {str(e)}"
                if self.logger:
                    self.logger.error(error_msg)
                return MemoryStatusResponse(
                    success=False,
                    message=error_msg
                )
        
        @self.app.post("/warm_up_squat", response_model=WarmUpSquatResponse)
        async def warm_up_squat_control(request: WarmUpSquatRequest):
            """控制热身蹲起动作 - 启动或停止"""
            if self.logger:
                self.logger.info(f'Received warm up squat request: {request.action}')
            
            try:
                if not self.command_executor.muto_interface:
                    return WarmUpSquatResponse(
                        success=False,
                        message="Muto interface not available",
                        action=request.action
                    )
                
                if request.action == "start":
                    # 启动热身蹲起动作
                    success, message, data = self.command_executor.muto_interface.warm_up_squat()
                    
                    response = WarmUpSquatResponse(
                        success=success,
                        message=message,
                        action="start",
                        data=data
                    )
                    
                elif request.action == "stop":
                    # 停止所有动作
                    success, message, data = self.command_executor.muto_interface.stop_action()
                    
                    response = WarmUpSquatResponse(
                        success=success,
                        message=message,
                        action="stop",
                        data=data
                    )
                    
                else:
                    return WarmUpSquatResponse(
                        success=False,
                        message=f"Invalid action: {request.action}. Use 'start' or 'stop'",
                        action=request.action
                    )
                
                if self.logger:
                    self.logger.info(f'Warm up squat {request.action} result: {response.success} - {response.message}')
                return response
                
            except Exception as e:
                error_msg = f"Error controlling warm up squat: {str(e)}"
                if self.logger:
                    self.logger.error(error_msg)
                return WarmUpSquatResponse(
                    success=False,
                    message=error_msg,
                    action=request.action
                )

        # ================ Point Front Actions ================
        @self.app.post("/actions/point_front", response_model=PointFrontResponse)
        async def point_front(request: PointFrontRequest):
            """指向左/右前方动作，根据 side 参数选择"""
            if self.logger:
                self.logger.info(f"Received point front request: side={request.side}")

            try:
                if not self.command_executor.muto_interface:
                    return PointFrontResponse(
                        success=False,
                        message="Muto interface not available",
                        side=request.side,
                        data=None
                    )

                kwargs: Dict[str, Any] = {}
                if request.hold_time is not None:
                    kwargs['hold_time'] = request.hold_time
                if request.amplitude is not None:
                    kwargs['amplitude'] = request.amplitude
                if request.speed_ms is not None:
                    kwargs['speed_ms'] = request.speed_ms

                if request.side.lower() == 'left':
                    success, message, data = await run_in_threadpool(self.command_executor.muto_interface.point_left_front, **kwargs)
                    response_side = 'left'
                elif request.side.lower() == 'right':
                    success, message, data = await run_in_threadpool(self.command_executor.muto_interface.point_right_front, **kwargs)
                    response_side = 'right'
                else:
                    return PointFrontResponse(
                        success=False,
                        message=f"Invalid side: {request.side}. Use 'left' or 'right'",
                        side=str(request.side),
                        data=None
                    )

                response = PointFrontResponse(
                    success=success,
                    message=message,
                    side=response_side,
                    data=data
                )

                if self.logger:
                    self.logger.info(f"Point front ({response_side}) result: {response.success} - {response.message}")
                return response
            except Exception as e:
                error_msg = f"Error executing point front: {str(e)}"
                if self.logger:
                    self.logger.error(error_msg)
                return PointFrontResponse(
                    success=False,
                    message=error_msg,
                    side=str(request.side),
                    data=None
                )

        # ================ Raise Hand Actions (compat) ================
        @self.app.post("/actions/raise_hand", response_model=RaiseHandResponse)
        async def raise_hand(request: RaiseHandRequest):
            """兼容端点：指向左/右前方（原名：举手）"""
            if self.logger:
                self.logger.info(f"Received raise hand request: side={request.side}")

            try:
                if not self.command_executor.muto_interface:
                    return RaiseHandResponse(
                        success=False,
                        message="Muto interface not available",
                        side=request.side,
                        data=None
                    )

                kwargs: Dict[str, Any] = {}
                if request.hold_time is not None:
                    kwargs['hold_time'] = request.hold_time
                if request.amplitude is not None:
                    kwargs['amplitude'] = request.amplitude
                if request.speed_ms is not None:
                    kwargs['speed_ms'] = request.speed_ms

                # 为修复左右错误和语义更准确，调用前方指向动作
                if request.side.lower() == 'left':
                    success, message, data = await run_in_threadpool(self.command_executor.muto_interface.point_left_front, **kwargs)
                    response_side = 'left'
                elif request.side.lower() == 'right':
                    success, message, data = await run_in_threadpool(self.command_executor.muto_interface.point_right_front, **kwargs)
                    response_side = 'right'
                else:
                    return RaiseHandResponse(
                        success=False,
                        message=f"Invalid side: {request.side}. Use 'left' or 'right'",
                        side=str(request.side),
                        data=None
                    )

                response = RaiseHandResponse(
                    success=success,
                    message=message,
                    side=response_side,
                    data=data
                )

                if self.logger:
                    self.logger.info(f"Raise hand ({response_side}) result: {response.success} - {response.message}")
                return response
            except Exception as e:
                error_msg = f"Error executing raise hand: {str(e)}"
                if self.logger:
                    self.logger.error(error_msg)
                return RaiseHandResponse(
                    success=False,
                    message=error_msg,
                    side=str(request.side),
                    data=None
                )
